#include "DLSInstrument.h"
#include "DLSSoundbank.h"
#include "DLSSample.h"
#include "DLSSampleOptions.h"
#include "DLSModulator.h"
#include "DLSSampleLoop.h"
#include "DLSRegion.h"
#include "io/FileInputStream.h"
#include "soundbank/Instrument.h"
#include "soundbank/Soundbank.h"
#include "soundbank/SoundbankResource.h"
#include "soundbank/ModelByteBuffer.h"

#include <stack>
using namespace yzrilyzr_io;
using namespace yzrilyzr_util;
using namespace yzrilyzr_collection;
using namespace yzrilyzr_array;
using namespace yzrilyzr_lang;
namespace yzrilyzr_soundbank{
	DLSSoundbank::DLSSoundbank(File & file){
		largeFormat=true;
		sampleFile=&file;
		FileInputStream is(file);
		readSoundbank(is);
	}
	DLSSoundbank::DLSSoundbank(InputStream & inputstream){
		readSoundbank(inputstream);
	}
	void DLSSoundbank::readSoundbank(InputStream & inputstream){
		RIFFReader riff(&inputstream);
		if(riff.getFormat() != "RIFF"){
			throw Exception(
				"Input stream is not a valid RIFF stream!");
		}
		if(riff.getType() != "DLS "){
			throw Exception(
				"Input stream is not a valid DLS soundbank!");
		}
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			if(chunk.getFormat() == ("LIST")){
				if(chunk.getType() == ("INFO"))
					readInfoChunk(chunk);
				if(chunk.getType() == ("lins"))
					readLinsChunk(chunk);
				if(chunk.getType() == ("wvpl"))
					readWvplChunk(chunk);
			} else{
				if(chunk.getFormat() == ("cdl ")){
					if(!readCdlChunk(chunk)){
						throw Exception(
							"DLS file isn't supported!");
					}
				}
				if(chunk.getFormat() == ("colh")){
					// skipped because we will load the entire bank into memory
					// int64_t instrumentcount = chunk.readUnsignedInt();
					// System.out.println("instrumentcount = "+ instrumentcount);
				}
				if(chunk.getFormat() == ("ptbl")){
					// Pool Table Chunk
					// skipped because we will load the entire bank into memory
				}
				if(chunk.getFormat() == ("vers")){
					major=chunk.readUnsignedInt();
					minor=chunk.readUnsignedInt();
				}
			}
		}

		for(auto & entry : temp_rgnassign.entries()){
			auto * region=entry.first;
			region->sample=samples.get(entry.second);
		}
	}

	bool DLSSoundbank::cdlIsQuerySupported(DLSID & uuid){
		return uuid == (DLSID_GMInHardware)
			|| uuid == (DLSID_GSInHardware)
			|| uuid == (DLSID_XGInHardware)
			|| uuid == (DLSID_SupportsDLS1)
			|| uuid == (DLSID_SupportsDLS2)
			|| uuid == (DLSID_SampleMemorySize)
			|| uuid == (DLSID_ManufacturersID)
			|| uuid == (DLSID_ProductID)
			|| uuid == (DLSID_SamplePlaybackRate);
	}

	int64_t DLSSoundbank::cdlQuery(DLSID & uuid){
		if(uuid == (DLSID_GMInHardware))
			return 1;
		if(uuid == (DLSID_GSInHardware))
			return 0;
		if(uuid == (DLSID_XGInHardware))
			return 0;
		if(uuid == (DLSID_SupportsDLS1))
			return 1;
		if(uuid == (DLSID_SupportsDLS2))
			return 1;
		if(uuid == (DLSID_SampleMemorySize))
			return 1024 * 1024 * 1024;
		if(uuid == (DLSID_ManufacturersID))
			return 0;
		if(uuid == (DLSID_ProductID))
			return 0;
		if(uuid == (DLSID_SamplePlaybackRate))
			return 44100;
		return 0;
	}

	// Reading cdl-ck Chunk
	// "cdl " chunk can only appear inside : DLS,lart,lar2,rgn,rgn2
	bool DLSSoundbank::readCdlChunk(RIFFReader & riff){
		DLSID uuid;
		int64_t x;
		int64_t y;
		std::stack<int64_t> stack;

		while(riff.available() != 0){
			int32_t opcode=riff.readUnsignedShort();
			switch(opcode){
				case DLS_CDL_AND:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((((x != 0) && (y != 0))?1:0));
					break;
				case DLS_CDL_OR:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((((x != 0) || (y != 0))?1:0));
					break;
				case DLS_CDL_XOR:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((((x != 0) ^ (y != 0))?1:0));
					break;
				case DLS_CDL_ADD:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((x + y));
					break;
				case DLS_CDL_SUBTRACT:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((x - y));
					break;
				case DLS_CDL_MULTIPLY:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((x * y));
					break;
				case DLS_CDL_DIVIDE:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((x / y));
					break;
				case DLS_CDL_LOGICAL_AND:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((((x != 0) && (y != 0))?1:0));
					break;
				case DLS_CDL_LOGICAL_OR:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push((((x != 0) || (y != 0))?1:0));
					break;
				case DLS_CDL_LT:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x < y)?1:0));
					break;
				case DLS_CDL_LE:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x <= y)?1:0));
					break;
				case DLS_CDL_GT:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x > y)?1:0));
					break;
				case DLS_CDL_GE:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x >= y)?1:0));
					break;
				case DLS_CDL_EQ:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x == y)?1:0));
					break;
				case DLS_CDL_NOT:
					x=stack.top();stack.pop();
					y=stack.top();stack.pop();
					stack.push(((x == 0)?1:0));
					break;
				case DLS_CDL_CONST:
					stack.push((riff.readUnsignedInt()));
					break;
				case DLS_CDL_QUERY:
					uuid=DLSID::read(riff);
					stack.push(cdlQuery(uuid));
					break;
				case DLS_CDL_QUERYSUPPORTED:
					uuid=DLSID::read(riff);
					stack.push((cdlIsQuerySupported(uuid)?1:0));
					break;
				default:
					break;
			}
		}
		if(stack.empty())
			return false;

		return stack.top() == 1;
	}

	void DLSSoundbank::readInfoChunk(RIFFReader & riff){
		info.name="";
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("INAM"))
				info.name=chunk.readString(chunk.available());
			else if(format == ("ICRD"))
				info.creationDate=chunk.readString(chunk.available());
			else if(format == ("IENG"))
				info.engineers=chunk.readString(chunk.available());
			else if(format == ("IPRD"))
				info.product=chunk.readString(chunk.available());
			else if(format == ("ICOP"))
				info.copyright=chunk.readString(chunk.available());
			else if(format == ("ICMT"))
				info.comments=chunk.readString(chunk.available());
			else if(format == ("ISFT"))
				info.tools=chunk.readString(chunk.available());
			else if(format == ("IARL"))
				info.archival_location=chunk.readString(chunk.available());
			else if(format == ("IART"))
				info.artist=chunk.readString(chunk.available());
			else if(format == ("ICMS"))
				info.commissioned=chunk.readString(chunk.available());
			else if(format == ("IGNR"))
				info.genre=chunk.readString(chunk.available());
			else if(format == ("IKEY"))
				info.keywords=chunk.readString(chunk.available());
			else if(format == ("IMED"))
				info.medium=chunk.readString(chunk.available());
			else if(format == ("ISBJ"))
				info.subject=chunk.readString(chunk.available());
			else if(format == ("ISRC"))
				info.source=chunk.readString(chunk.available());
			else if(format == ("ISRF"))
				info.source_form=chunk.readString(chunk.available());
			else if(format == ("ITCH"))
				info.technician=chunk.readString(chunk.available());
		}
	}

	void DLSSoundbank::readLinsChunk(RIFFReader & riff){
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			if(chunk.getFormat() == ("LIST")){
				if(chunk.getType() == ("ins "))
					readInsChunk(chunk);
			}
		}
	}

	void DLSSoundbank::readInsChunk(RIFFReader & riff){
		auto instrument=std::make_shared<DLSInstrument>(this);

		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("LIST")){
				if(chunk.getType() == ("INFO")){
					readInsInfoChunk(*instrument, chunk);
				}
				if(chunk.getType() == ("lrgn")){
					while(chunk.hasNextChunk()){
						auto & subchunk=*chunk.nextChunk();
						if(subchunk.getFormat() == ("LIST")){
							if(subchunk.getType() == ("rgn ")){
								auto split=std::make_shared < DLSRegion>();
								if(readRgnChunk(*split, subchunk))
									(*instrument).getRegions().add(split);
							}
							if(subchunk.getType() == ("rgn2")){
								// support for DLS level 2 regions
								auto split=std::make_shared < DLSRegion>();
								if(readRgnChunk(*split, subchunk))
									(*instrument).getRegions().add(split);
							}
						}
					}
				}
				if(chunk.getType() == ("lart")){
					ArrayList<std::shared_ptr<DLSModulator>> modlist;
					while(chunk.hasNextChunk()){
						auto & subchunk=*chunk.nextChunk();
						if(chunk.getFormat() == ("cdl ")){
							if(!readCdlChunk(chunk)){
								modlist.clear();
								break;
							}
						}
						if(subchunk.getFormat() == ("art1"))
							readArt1Chunk(modlist, subchunk);
					}
					(*instrument).getModulators().addAll(modlist);
				}
				if(chunk.getType() == ("lar2")){
					// support for DLS level 2 ART
					ArrayList<std::shared_ptr<DLSModulator>> modlist;
					while(chunk.hasNextChunk()){
						auto & subchunk=*chunk.nextChunk();
						if(chunk.getFormat() == ("cdl ")){
							if(!readCdlChunk(chunk)){
								modlist.clear();
								break;
							}
						}
						if(subchunk.getFormat() == ("art2"))
							readArt2Chunk(modlist, subchunk);
					}
					(*instrument).getModulators().addAll(modlist);
				}
			} else{
				if(format == ("dlid")){
					ByteArray temp(16);
					chunk.readFully(temp);
					instrument->guid=temp;
				}
				if(format == ("insh")){
					chunk.readUnsignedInt(); // Read Region Count - ignored

					int32_t bank=chunk.read();             // LSB
					bank+=(chunk.read() & 127) << 7;   // MSB
					chunk.read(); // Read Reserved byte
					int32_t drumins=chunk.read();          // Drum Instrument

					int32_t id=chunk.read() & 127; // Read only first 7 bits
					chunk.read(); // Read Reserved byte
					chunk.read(); // Read Reserved byte
					chunk.read(); // Read Reserved byte

					instrument->bank=bank;
					instrument->preset=id;
					instrument->druminstrument=(drumins & 128) > 0;
					//System.out.println("bank="+bank+" drumkit="+drumkit
					//        +" id="+id);
				}
			}
		}
		instruments.add(instrument);
	}

	void DLSSoundbank::readArt1Chunk(ArrayList<std::shared_ptr<DLSModulator>> & modulators, RIFFReader & riff){
		int64_t size=riff.readUnsignedInt();
		int64_t count=riff.readUnsignedInt();

		if(size - 8 != 0)
			riff.skip(size - 8);

		for(int32_t i=0; i < count; i++){
			auto modulator=std::make_shared<DLSModulator>();
			modulator->version=1;
			modulator->source=riff.readUnsignedShort();
			modulator->control=riff.readUnsignedShort();
			modulator->destination=riff.readUnsignedShort();
			modulator->transform=riff.readUnsignedShort();
			modulator->scale=riff.readInt();
			modulators.add(modulator);
		}
	}

	void DLSSoundbank::readArt2Chunk(ArrayList< std::shared_ptr<DLSModulator>> & modulators, RIFFReader & riff){
		int64_t size=riff.readUnsignedInt();
		int64_t count=riff.readUnsignedInt();

		if(size - 8 != 0)
			riff.skip(size - 8);

		for(int32_t i=0; i < count; i++){
			auto modulator=std::make_shared<DLSModulator>();
			modulator->version=2;
			modulator->source=riff.readUnsignedShort();
			modulator->control=riff.readUnsignedShort();
			modulator->destination=riff.readUnsignedShort();
			modulator->transform=riff.readUnsignedShort();
			modulator->scale=riff.readInt();
			modulators.add(modulator);
		}
	}
	bool DLSSoundbank::readRgnChunk(DLSRegion & split, RIFFReader & riff){
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("LIST")){
				if(chunk.getType() == ("lart")){
					ArrayList<std::shared_ptr<DLSModulator>> modlist;
					while(chunk.hasNextChunk()){
						auto & subchunk=*chunk.nextChunk();
						if(chunk.getFormat() == ("cdl ")){
							if(!readCdlChunk(chunk)){
								modlist.clear();
								break;
							}
						}
						if(subchunk.getFormat() == ("art1"))
							readArt1Chunk(modlist, subchunk);
					}
					split.getModulators().addAll(modlist);
				}
				if(chunk.getType() == ("lar2")){
					// support for DLS level 2 ART
					ArrayList<std::shared_ptr<DLSModulator>> modlist;
					while(chunk.hasNextChunk()){
						auto & subchunk=*chunk.nextChunk();
						if(chunk.getFormat() == ("cdl ")){
							if(!readCdlChunk(chunk)){
								modlist.clear();
								break;
							}
						}
						if(subchunk.getFormat() == ("art2"))
							readArt2Chunk(modlist, subchunk);
					}
					split.getModulators().addAll(modlist);
				}
			} else{
				if(format == ("cdl ")){
					if(!readCdlChunk(chunk))
						return false;
				}
				if(format == ("rgnh")){
					split.keyfrom=chunk.readUnsignedShort();
					split.keyto=chunk.readUnsignedShort();
					split.velfrom=chunk.readUnsignedShort();
					split.velto=chunk.readUnsignedShort();
					split.options=chunk.readUnsignedShort();
					split.exclusiveClass=chunk.readUnsignedShort();
				}
				if(format == ("wlnk")){
					split.fusoptions=chunk.readUnsignedShort();
					split.phasegroup=chunk.readUnsignedShort();
					split.channel=chunk.readUnsignedInt();
					int64_t sampleid=chunk.readUnsignedInt();
					temp_rgnassign.put(&split, sampleid);
				}
				if(format == ("wsmp")){
					split.sampleoptions=std::make_shared<DLSSampleOptions>();
					readWsmpChunk(*split.sampleoptions, chunk);
				}
			}
		}
		return true;
	}

	void DLSSoundbank::readWsmpChunk(DLSSampleOptions & sampleOptions, RIFFReader & riff){
		int64_t size=riff.readUnsignedInt();
		sampleOptions.unitynote=riff.readUnsignedShort();
		sampleOptions.finetune=riff.readShort();
		sampleOptions.attenuation=riff.readInt();
		sampleOptions.options=riff.readUnsignedInt();
		int64_t loops=riff.readInt();

		if(size > 20)
			riff.skip(size - 20);

		for(int32_t i=0; i < loops; i++){
			auto loop=std::make_shared<DLSSampleLoop>();
			int64_t size2=riff.readUnsignedInt();
			loop->type=riff.readUnsignedInt();
			loop->start=riff.readUnsignedInt();
			loop->length=riff.readUnsignedInt();
			sampleOptions.loops.add(loop);
			if(size2 > 16)
				riff.skip(size2 - 16);
		}
	}

	void DLSSoundbank::readInsInfoChunk(DLSInstrument & dlsinstrument, RIFFReader & riff){
		dlsinstrument.info.name="";
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("INAM")){
				dlsinstrument.info.name=chunk.readString(chunk.available());
			} else if(format == ("ICRD")){
				dlsinstrument.info.creationDate=
					chunk.readString(chunk.available());
			} else if(format == ("IENG")){
				dlsinstrument.info.engineers=
					chunk.readString(chunk.available());
			} else if(format == ("IPRD")){
				dlsinstrument.info.product=chunk.readString(chunk.available());
			} else if(format == ("ICOP")){
				dlsinstrument.info.copyright=
					chunk.readString(chunk.available());
			} else if(format == ("ICMT")){
				dlsinstrument.info.comments=
					chunk.readString(chunk.available());
			} else if(format == ("ISFT")){
				dlsinstrument.info.tools=chunk.readString(chunk.available());
			} else if(format == ("IARL")){
				dlsinstrument.info.archival_location=
					chunk.readString(chunk.available());
			} else if(format == ("IART")){
				dlsinstrument.info.artist=chunk.readString(chunk.available());
			} else if(format == ("ICMS")){
				dlsinstrument.info.commissioned=
					chunk.readString(chunk.available());
			} else if(format == ("IGNR")){
				dlsinstrument.info.genre=chunk.readString(chunk.available());
			} else if(format == ("IKEY")){
				dlsinstrument.info.keywords=
					chunk.readString(chunk.available());
			} else if(format == ("IMED")){
				dlsinstrument.info.medium=chunk.readString(chunk.available());
			} else if(format == ("ISBJ")){
				dlsinstrument.info.subject=chunk.readString(chunk.available());
			} else if(format == ("ISRC")){
				dlsinstrument.info.source=chunk.readString(chunk.available());
			} else if(format == ("ISRF")){
				dlsinstrument.info.source_form=
					chunk.readString(chunk.available());
			} else if(format == ("ITCH")){
				dlsinstrument.info.technician=
					chunk.readString(chunk.available());
			}
		}
	}

	void DLSSoundbank::readWvplChunk(RIFFReader & riff){
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			if(chunk.getFormat() == ("LIST")){
				if(chunk.getType() == ("wave"))
					readWaveChunk(chunk);
			}
		}
	}

	void DLSSoundbank::readWaveChunk(RIFFReader & riff){
		auto sample=std::make_shared < DLSSample>(this);

		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("LIST")){
				if(chunk.getType() == ("INFO")){
					readWaveInfoChunk(*sample, chunk);
				}
			} else{
				if(format == ("dlid")){
					ByteArray temp(16);
					chunk.readFully(temp);
					sample->guid=temp;
				}
				if(format == ("fmt ")){
					int32_t sampleformat=chunk.readUnsignedShort();
					if(sampleformat != 1 && sampleformat != 3){
						throw  Exception("Only PCM samples are supported!");
					}
					sample->channels=chunk.readUnsignedShort();
					sample->samplerate=chunk.readUnsignedInt();
					sample->framerate=chunk.readUnsignedInt();
					sample->framesize=chunk.readUnsignedShort();
					sample->bits=chunk.readUnsignedShort();
					if(sampleformat == 1){
						if(sample->bits == 8){
							sample->format=DLSSampleFormat::UNSIGNED;
						} else{
							sample->format=DLSSampleFormat::SIGNED;
						}
					} else if(sampleformat == 3){
						sample->format=DLSSampleFormat::FLOAT;
					}
				}
				if(format == ("data")){
					if(largeFormat){
						sample->setData(std::make_shared<ModelByteBuffer>(sampleFile,
																		  chunk.getFilePointer(), chunk.available()));
					} else{
						auto buffer=std::make_shared<ByteArray>(chunk.available());
						//  chunk.read(buffer);
						sample->setData(buffer);

						int32_t read=0;
						int32_t avail=chunk.available();
						while(read != avail){
							if(avail - read > 65536){
								chunk.readFully(*buffer, read, 65536);
								read+=65536;
							} else{
								chunk.readFully(*buffer, read, avail - read);
								read=avail;
							}
						}
					}
				}

				if(format == ("wsmp")){
					sample->sampleoptions=std::make_shared< DLSSampleOptions>();
					readWsmpChunk(*sample->sampleoptions, chunk);
				}
			}
		}

		samples.add(sample);
	}

	void DLSSoundbank::readWaveInfoChunk(DLSSample & dlssample, RIFFReader & riff){
		dlssample.info.name="";
		while(riff.hasNextChunk()){
			auto & chunk=*riff.nextChunk();
			String format=chunk.getFormat();
			if(format == ("INAM")){
				dlssample.info.name=chunk.readString(chunk.available());
			} else if(format == ("ICRD")){
				dlssample.info.creationDate=
					chunk.readString(chunk.available());
			} else if(format == ("IENG")){
				dlssample.info.engineers=chunk.readString(chunk.available());
			} else if(format == ("IPRD")){
				dlssample.info.product=chunk.readString(chunk.available());
			} else if(format == ("ICOP")){
				dlssample.info.copyright=chunk.readString(chunk.available());
			} else if(format == ("ICMT")){
				dlssample.info.comments=chunk.readString(chunk.available());
			} else if(format == ("ISFT")){
				dlssample.info.tools=chunk.readString(chunk.available());
			} else if(format == ("IARL")){
				dlssample.info.archival_location=
					chunk.readString(chunk.available());
			} else if(format == ("IART")){
				dlssample.info.artist=chunk.readString(chunk.available());
			} else if(format == ("ICMS")){
				dlssample.info.commissioned=
					chunk.readString(chunk.available());
			} else if(format == ("IGNR")){
				dlssample.info.genre=chunk.readString(chunk.available());
			} else if(format == ("IKEY")){
				dlssample.info.keywords=chunk.readString(chunk.available());
			} else if(format == ("IMED")){
				dlssample.info.medium=chunk.readString(chunk.available());
			} else if(format == ("ISBJ")){
				dlssample.info.subject=chunk.readString(chunk.available());
			} else if(format == ("ISRC")){
				dlssample.info.source=chunk.readString(chunk.available());
			} else if(format == ("ISRF")){
				dlssample.info.source_form=chunk.readString(chunk.available());
			} else if(format == ("ITCH")){
				dlssample.info.technician=chunk.readString(chunk.available());
			}
		}
	}

	void DLSSoundbank::save(String & name){
		/*try (RIFFWriter writer=new RIFFWriter(name, "DLS ")){
			writeSoundbank(writer);
		}*/
	}

	void DLSSoundbank::save(File & file){
		/*try (RIFFWriter writer=new RIFFWriter(file, "DLS ")){
			writeSoundbank(writer);
		}*/
	}

	void DLSSoundbank::save(OutputStream & out){
		/*try (RIFFWriter writer=new RIFFWriter(out, "DLS ")){
			writeSoundbank(writer);
		}*/
	}

	//void DLSSoundbank::writeSoundbank(RIFFWriter writer){
	//	RIFFWriter colh_chunk=writer.writeChunk("colh");
	//	colh_chunk.writeUnsignedInt(instruments.size());

	//	if(major != -1 && minor != -1){
	//		RIFFWriter vers_chunk=writer.writeChunk("vers");
	//		vers_chunk.writeUnsignedInt(major);
	//		vers_chunk.writeUnsignedInt(minor);
	//	}

	//	writeInstruments(writer.writeList("lins"));

	//	RIFFWriter ptbl=writer.writeChunk("ptbl");
	//	ptbl.writeUnsignedInt(8);
	//	ptbl.writeUnsignedInt(samples.size());
	//	int64_t ptbl_offset=writer.getFilePointer();
	//	for(int32_t i=0; i < samples.size(); i++)
	//		ptbl.writeUnsignedInt(0);

	//	RIFFWriter wvpl=writer.writeList("wvpl");
	//	int64_t off=wvpl.getFilePointer();
	//	List<Long> offsettable=new ArrayList<>();
	//	for(DLSSample sample:samples){
	//		offsettable.add(Long.valueOf(wvpl.getFilePointer() - off));
	//		writeSample(wvpl.writeList("wave"), sample);
	//	}

	//	// small cheat, we are going to rewrite data back in wvpl
	//	int64_t bak=writer.getFilePointer();
	//	writer.seek(ptbl_offset);
	//	writer.setWriteOverride(true);
	//	for(Long offset:offsettable)
	//		writer.writeUnsignedInt(offset.int64_tValue());
	//	writer.setWriteOverride(false);
	//	writer.seek(bak);

	//	writeInfo(writer.writeList("INFO"), info);
	//}

	//void DLSSoundbank::writeSample(RIFFWriter writer, DLSSample sample){
	//	AudioFormat audioformat=sample.getFormat();

	//	Encoding encoding=audioformat.getEncoding();
	//	float sampleRate=audioformat.getSampleRate();
	//	int32_t sampleSizeInBits=audioformat.getSampleSizeInBits();
	//	int32_t channels=audioformat.getChannels();
	//	int32_t frameSize=audioformat.getFrameSize();
	//	float frameRate=audioformat.getFrameRate();
	//	bool bigEndian=audioformat.isBigEndian();

	//	bool convert_needed=false;

	//	if(audioformat.getSampleSizeInBits() == 8){
	//		if(!encoding == (Encoding.PCM_UNSIGNED)){
	//			encoding=Encoding.PCM_UNSIGNED;
	//			convert_needed=true;
	//		}
	//	} else{
	//		if(!encoding == (Encoding.PCM_SIGNED)){
	//			encoding=Encoding.PCM_SIGNED;
	//			convert_needed=true;
	//		}
	//		if(bigEndian){
	//			bigEndian=false;
	//			convert_needed=true;
	//		}
	//	}

	//	if(convert_needed){
	//		audioformat=new AudioFormat(encoding, sampleRate,
	//									sampleSizeInBits, channels, frameSize, frameRate, bigEndian);
	//	}

	//	// fmt
	//	RIFFWriter fmt_chunk=writer.writeChunk("fmt ");
	//	int32_t sampleformat=0;
	//	if(audioformat.getEncoding() == (Encoding.PCM_UNSIGNED))
	//		sampleformat=1;
	//	else if(audioformat.getEncoding() == (Encoding.PCM_SIGNED))
	//		sampleformat=1;
	//	else if(audioformat.getEncoding() == (Encoding.PCM_FLOAT))
	//		sampleformat=3;

	//	fmt_chunk.writeUnsignedShort(sampleformat);
	//	fmt_chunk.writeUnsignedShort(audioformat.getChannels());
	//	fmt_chunk.writeUnsignedInt((int64_t)audioformat.getSampleRate());
	//	int64_t srate=((int64_t)audioformat.getFrameRate()) * audioformat.getFrameSize();
	//	fmt_chunk.writeUnsignedInt(srate);
	//	fmt_chunk.writeUnsignedShort(audioformat.getFrameSize());
	//	fmt_chunk.writeUnsignedShort(audioformat.getSampleSizeInBits());
	//	fmt_chunk.write(0);
	//	fmt_chunk.write(0);

	//	writeSampleOptions(writer.writeChunk("wsmp"), sample.sampleoptions);

	//	if(convert_needed){
	//		RIFFWriter data_chunk=writer.writeChunk("data");
	//		AudioInputStream stream=AudioSystem.getAudioInputStream(
	//			audioformat, (AudioInputStream)sample.getData());
	//		stream.transferTo(data_chunk);
	//	} else{
	//		RIFFWriter data_chunk=writer.writeChunk("data");
	//		ModelByteBuffer databuff=sample.getDataBuffer();
	//		databuff.writeTo(data_chunk);
	//		/*
	//		data_chunk.write(databuff.array(),
	//		databuff.arrayOffset(),
	//		databuff.capacity());
	//		 */
	//	}

	//	writeInfo(writer.writeList("INFO"), sample.info);
	//}

	//void DLSSoundbank::writeInstruments(RIFFWriter writer){
	//	for(DLSInstrument instrument:instruments){
	//		writeInstrument(writer.writeList("ins "), instrument);
	//	}
	//}

	//void DLSSoundbank::writeInstrument(RIFFWriter writer, DLSInstrument instrument){
	//	int32_t art1_count=0;
	//	int32_t art2_count=0;
	//	for(DLSModulator modulator:instrument.getModulators()){
	//		if(modulator.version == 1)
	//			art1_count++;
	//		if(modulator.version == 2)
	//			art2_count++;
	//	}
	//	for(DLSRegion region:instrument.regions){
	//		for(DLSModulator modulator:region.getModulators()){
	//			if(modulator.version == 1)
	//				art1_count++;
	//			if(modulator.version == 2)
	//				art2_count++;
	//		}
	//	}

	//	int32_t version=1;
	//	if(art2_count > 0)
	//		version=2;

	//	RIFFWriter insh_chunk=writer.writeChunk("insh");
	//	insh_chunk.writeUnsignedInt(instrument.getRegions().size());
	//	insh_chunk.writeUnsignedInt(instrument.bank +
	//								(instrument.druminstrument?2147483648L:0));
	//	insh_chunk.writeUnsignedInt(instrument.preset);

	//	RIFFWriter lrgn=writer.writeList("lrgn");
	//	for(DLSRegion region:instrument.regions)
	//		writeRegion(lrgn, region, version);

	//	writeArticulators(writer, instrument.getModulators());

	//	writeInfo(writer.writeList("INFO"), instrument.info);
	//}

	//void DLSSoundbank::writeArticulators(RIFFWriter writer,
	//									 ArrayList<std::shared_ptr<DLSModulator>> modulators
	//){
	//	int32_t art1_count=0;
	//	int32_t art2_count=0;
	//	for(DLSModulator modulator:modulators){
	//		if(modulator.version == 1)
	//			art1_count++;
	//		if(modulator.version == 2)
	//			art2_count++;
	//	}
	//	if(art1_count > 0){
	//		RIFFWriter lar1=writer.writeList("lart");
	//		RIFFWriter art1=lar1.writeChunk("art1");
	//		art1.writeUnsignedInt(8);
	//		art1.writeUnsignedInt(art1_count);
	//		for(DLSModulator modulator:modulators){
	//			if(modulator.version == 1){
	//				art1.writeUnsignedShort(modulator.source);
	//				art1.writeUnsignedShort(modulator.control);
	//				art1.writeUnsignedShort(modulator.destination);
	//				art1.writeUnsignedShort(modulator.transform);
	//				art1.writeInt(modulator.scale);
	//			}
	//		}
	//	}
	//	if(art2_count > 0){
	//		RIFFWriter lar2=writer.writeList("lar2");
	//		RIFFWriter art2=lar2.writeChunk("art2");
	//		art2.writeUnsignedInt(8);
	//		art2.writeUnsignedInt(art2_count);
	//		for(DLSModulator modulator:modulators){
	//			if(modulator.version == 2){
	//				art2.writeUnsignedShort(modulator.source);
	//				art2.writeUnsignedShort(modulator.control);
	//				art2.writeUnsignedShort(modulator.destination);
	//				art2.writeUnsignedShort(modulator.transform);
	//				art2.writeInt(modulator.scale);
	//			}
	//		}
	//	}
	//}

	//void DLSSoundbank::writeRegion(RIFFWriter writer, DLSRegion region, int32_t version){
	//	RIFFWriter rgns=null;
	//	if(version == 1)
	//		rgns=writer.writeList("rgn ");
	//	if(version == 2)
	//		rgns=writer.writeList("rgn2");
	//	if(rgns == null)
	//		return;

	//	RIFFWriter rgnh=rgns.writeChunk("rgnh");
	//	rgnh.writeUnsignedShort(region.keyfrom);
	//	rgnh.writeUnsignedShort(region.keyto);
	//	rgnh.writeUnsignedShort(region.velfrom);
	//	rgnh.writeUnsignedShort(region.velto);
	//	rgnh.writeUnsignedShort(region.options);
	//	rgnh.writeUnsignedShort(region.exclusiveClass);

	//	if(region.sampleoptions != null)
	//		writeSampleOptions(rgns.writeChunk("wsmp"), region.sampleoptions);

	//	if(region.sample != null){
	//		if(samples.indexOf(region.sample) != -1){
	//			RIFFWriter wlnk=rgns.writeChunk("wlnk");
	//			wlnk.writeUnsignedShort(region.fusoptions);
	//			wlnk.writeUnsignedShort(region.phasegroup);
	//			wlnk.writeUnsignedInt(region.channel);
	//			wlnk.writeUnsignedInt(samples.indexOf(region.sample));
	//		}
	//	}
	//	writeArticulators(rgns, region.getModulators());
	//	rgns.close();
	//}

	//void DLSSoundbank::writeSampleOptions(RIFFWriter wsmp,
	//									  DLSSampleOptions sampleoptions){
	//	wsmp.writeUnsignedInt(20);
	//	wsmp.writeUnsignedShort(sampleoptions.unitynote);
	//	wsmp.writeShort(sampleoptions.finetune);
	//	wsmp.writeInt(sampleoptions.attenuation);
	//	wsmp.writeUnsignedInt(sampleoptions.options);
	//	wsmp.writeInt(sampleoptions.loops.size());

	//	for(DLSSampleLoop loop:sampleoptions.loops){
	//		wsmp.writeUnsignedInt(16);
	//		wsmp.writeUnsignedInt(loop.type);
	//		wsmp.writeUnsignedInt(loop.start);
	//		wsmp.writeUnsignedInt(loop.length);
	//	}
	//}

	//void DLSSoundbank::writeInfoStringChunk(RIFFWriter writer,
	//										String name, String value){
	//	if(value == null)
	//		return;
	//	RIFFWriter chunk=writer.writeChunk(name);
	//	chunk.writeString(value);
	//	int32_t len=value.getBytes(US_ASCII).length;
	//	chunk.write(0);
	//	len++;
	//	if(len % 2 != 0)
	//		chunk.write(0);
	//}

	//void DLSSoundbank::writeInfo(RIFFWriter writer, DLSInfo info){
	//	writeInfoStringChunk(writer, "INAM", info.name);
	//	writeInfoStringChunk(writer, "ICRD", info.creationDate);
	//	writeInfoStringChunk(writer, "IENG", info.engineers);
	//	writeInfoStringChunk(writer, "IPRD", info.product);
	//	writeInfoStringChunk(writer, "ICOP", info.copyright);
	//	writeInfoStringChunk(writer, "ICMT", info.comments);
	//	writeInfoStringChunk(writer, "ISFT", info.tools);
	//	writeInfoStringChunk(writer, "IARL", info.archival_location);
	//	writeInfoStringChunk(writer, "IART", info.artist);
	//	writeInfoStringChunk(writer, "ICMS", info.commissioned);
	//	writeInfoStringChunk(writer, "IGNR", info.genre);
	//	writeInfoStringChunk(writer, "IKEY", info.keywords);
	//	writeInfoStringChunk(writer, "IMED", info.medium);
	//	writeInfoStringChunk(writer, "ISBJ", info.subject);
	//	writeInfoStringChunk(writer, "ISRC", info.source);
	//	writeInfoStringChunk(writer, "ISRF", info.source_form);
	//	writeInfoStringChunk(writer, "ITCH", info.technician);
	//}
	std::vector<std::shared_ptr<SoundbankResource>>  DLSSoundbank::getResources(){
		auto resources=std::vector<std::shared_ptr<SoundbankResource>>();
		int32_t j=0;
		for(auto & sample : samples){ resources.push_back(sample); }
		return resources;
	}

	std::vector<std::shared_ptr<Instrument>>  DLSSoundbank::getInstruments(){
		auto arr=std::vector<std::shared_ptr<Instrument>>();
		for(auto & obj : instruments)arr.push_back(obj);
		return arr;
	}

	yzrilyzr_array::Array < std::shared_ptr < DLSSample>>  DLSSoundbank::getSamples(){
		return samples.toArray();
	}

	std::shared_ptr < Instrument>  DLSSoundbank::getInstrument(int32_t program, int32_t bank){
		for(auto & instrument : instruments){
			int32_t program2=instrument->getPreset();
			int32_t bank2=instrument->getBank();
			if(program == program2 && bank == bank2){
				return instrument;
			}
		}
		return nullptr;
	}

	void  DLSSoundbank::addResource(std::shared_ptr <SoundbankResource > resource){
		if(auto paramRegPtr=std::dynamic_pointer_cast<DLSInstrument>(resource))
			instruments.add(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<DLSSample>(resource))
			samples.add(paramRegPtr);
	}

	void  DLSSoundbank::removeResource(std::shared_ptr <SoundbankResource >  resource){
		if(auto paramRegPtr=std::dynamic_pointer_cast<DLSInstrument>(resource))
			instruments.remove(paramRegPtr);
		if(auto paramRegPtr=std::dynamic_pointer_cast<DLSSample>(resource))
			samples.remove(paramRegPtr);
	}

	void  DLSSoundbank::addInstrument(std::shared_ptr < DLSInstrument > resource){
		instruments.add(resource);
	}

	void  DLSSoundbank::removeInstrument(std::shared_ptr < DLSInstrument > resource){
		instruments.remove(resource);
	}
}